# vim:ts=4:sw=4:noet:
""" Connection Manager to remote devices """

import threading
import logging
import time
import glob
import sys
from c.remote.connection import RemoteTcpConnection, RemoteSerialConnection

class DeviceChecker:
	def __init__(self, dm):
		self.dm = dm
		self.connections = {}

	def addConnection(self, id, conn, bootable):
		self.connections[id] = (conn, bootable)
		self.dm.onConnect(id, conn, bootable)

	def removeConnection(self, id):
		self.dm.onDisconnect(id, self.getConnection(id))
		del self.connections[id]

	def hasConnection(self, id):
		return id in self.connections

	def getConnection(self, id):
		return self.connections[id][0]

	def isConnectionBootable(self, id):
		return self.connections[id][1]

	def check(self):
		pass

class SimDeviceChecker(DeviceChecker):
	def __init__(self, dm):
		DeviceChecker.__init__(self, dm)
		self.conn = RemoteTcpConnection(connect=False)

	def check(self):
		if self.conn.sock:
			try:
				self.dm.mc.r.getBootState()
			except Exception, e:
				self.conn.sock = None
				self.dm.releaseConnection()
				self.removeConnection("sim:tcp")
		else:
			if self.conn.connect():
				self.addConnection("sim:tcp", self.conn, False)

class SerialDeviceChecker(DeviceChecker):
	def __init__(self, dm):
		DeviceChecker.__init__(self, dm)

	def check(self):
		if sys.platform=="win32":
			ports = []
			for i in range(16):
				ports.append(i)
		else:
			ports = glob.glob("/dev/tty.usbmodem*")
			ports.extend(glob.glob("/dev/ttyUSB*"))
		connkeys = self.connections.keys()
		setNew = False
		# check available ports
		for p in ports:
			oldconn = self.dm.mc.r.conn
			if p in connkeys:
				# if we're already connected to this port, check for disconnects
				connkeys.remove(p)
				conn = self.getConnection(p)
				self.dm.mc.r.conn = conn
				try:
					self.dm.mc.r.getBootState()
				except:
					self.dm.releaseConnection()
					cn = self.getConnection(p)
					self.removeConnection(p)
					cn.close()
			else:
				# otherwise check if there's a bot attached
				try:
					conn = RemoteSerialConnection(p)
					self.dm.mc.r.conn = conn
					boot = self.dm.mc.r.getBootState()
					setNew = True
					self.addConnection(p, conn, boot)
				except:
					self.dm.releaseConnection()
			if not setNew:
				self.dm.mc.r.conn = oldconn
		# remaining conns are invalid because the port no longer exists
		for p in connkeys:
			cn = self.getConnection(p)
			self.removeConnection(p)
			cn.close()

class DeviceConnection:
	pass

class DeviceManager:
	def __init__(self, mc):
		self.mc = mc
		self.deviceCheckers = [
				SimDeviceChecker(self),
				SerialDeviceChecker(self),
				]
		self.mc.tm.createTask("checker", self.checkDevices, start=False)
		self.stop = False
		self.connected = False
		self.curConnection = None
		self.connLock = threading.Semaphore()
		self.count = -5

	def start(self):
		self.mc.tm.startTask("checker")

	def aquireConnection(self):
		self.connLock.acquire()
		return self.curConnection

	def releaseConnection(self):
		self.connLock.release()

	def shutdown(self):
		logging.info("[dm] Shutting down")
		self.stop = True

	def currentConnection(self):
		return self.curConnection

	def checkForFirmwareUpgrade(self):
		revstr = self.mc.r.getVersion()[2][1:]
		if not revstr:
			return
		rev = int(revstr)
		p, a = self.mc.r.getPlatform()
		fw = glob.glob("fw/%s-%s-*.bin" % (p.lower(), a.lower()))
		upgrade = False
		ffile = ""
		for f in fw:
			v = f[3:-4].split("-")
			frev = int(v[2])
			if frev>rev:
				ffile = f
				upgrade = True
		if upgrade:
			self.mc.showDeviceUpgrade(frev, ffile)

	def refresh(self):
		for dc in self.deviceCheckers:
			for c in dc.connections:
				self.onConnect(c, dc.getConnection(c), dc.isConnectionBootable(c))

	def onConnect(self, id, conn, bootable):
		self.curConnection = conn
		self.mc.r.conn = conn
		if bootable:
			name = "<giggleboot>"
			self.mc.addDevice(id, name, conn)
		else:
			name = self.mc.r.getPlatform()[0]
			self.mc.addDevice(id, str(name), conn)
			self.checkForFirmwareUpgrade()
		logging.info("[dm] Connected %s" % conn)

	def onDisconnect(self, id, conn):
		self.curConnection = None
		self.mc.r.conn = None
		self.mc.removeDevice(id)
		logging.info("[dm] Disconnected %s" % conn)

	def checkDevices(self):
		while not self.stop:
			self.count += 1
			if self.count % 20 == 0:
				for dc in self.deviceCheckers:
					dc.check()
			time.sleep(0.1)
